package com.homestead.aspect;

import com.homestead.annotation.MethodRedisLock;
import com.homestead.result.RedisLockResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author yhb
 * @date 2021-03-16 11:05
 */

@Component
@Aspect
public class MyRedisLockAspect {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.homestead.annotation.MethodRedisLock) && execution(com.homestead.result.RedisLockResult *..*.*(String,..)) && args(lockNameSuffix,..)")
    private void pointcutForRedisLock(String lockNameSuffix) {}


    @Around(value = "pointcutForRedisLock(lockNameSuffix)", argNames = "joinPoint,lockNameSuffix")
    public Object aroundForRedisLock(ProceedingJoinPoint joinPoint, String lockNameSuffix) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        MethodRedisLock annotation = method.getAnnotation(MethodRedisLock.class);

        String keyName = String.format("%s%s", annotation.lockNamePrefix(), lockNameSuffix);
        RLock lock = redissonClient.getLock(keyName);

        boolean lockResult = lock.tryLock(annotation.waitTime(), annotation.leaseTime(), annotation.timeUnit());
        if (!lockResult) {
            return RedisLockResult.fail();
        }

        try {
            return joinPoint.proceed();
        } finally {
            try {
                lock.unlock();
            } catch (Exception e) {
                logger.error("RedisUtil#executeTryLock解锁失败，lockName={}", keyName, e);
            }
        }
    }
}
